package com.atguigu.gmall.wms.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.exception.OrderException;
import com.atguigu.gmall.wms.vo.SkuLockVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gmall.common.bean.PageResultVo;
import com.atguigu.gmall.common.bean.PageParamVo;

import com.atguigu.gmall.wms.mapper.WareSkuMapper;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuMapper, WareSkuEntity> implements WareSkuService {

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private WareSkuMapper wareSkuMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String LOCK_PREFIX = "stock:lock:";
    private static final String KEY_PREFIX = "stock:info:";

    @Override
    public PageResultVo queryPage(PageParamVo paramVo) {
        IPage<WareSkuEntity> page = this.page(
                paramVo.getPage(),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageResultVo(page);
    }

    // 锁库存与解锁
    @Transactional
    @Override
    public List<SkuLockVo> checkAndLock(List<SkuLockVo> lockVos, String orderToken) {

        //判断是否为空
        if (CollectionUtils.isEmpty(lockVos)){
            throw new OrderException("请选择要购买的商品");
        }

        //遍历所有需要锁定的库存 校验并锁定
        lockVos.forEach(lockVo -> {
            this.lockStock(lockVo);
        });

        //如果存在锁定失败的库存 则所有锁定成功的库存都要解锁
        if (lockVos.stream().anyMatch(lockVo -> !lockVo.getLock())){
            //获取锁定成功的库存 并遍历解锁库存
            lockVos.stream().filter(SkuLockVo::getLock).collect(Collectors.toList()).forEach(lockVo -> {
                this.wareSkuMapper.unlock(lockVo.getCount(),lockVo.getWareId());
            });
            return lockVos;
        }
        //为了解锁或者减库存方便 把锁定信息存放到redis
        redisTemplate.opsForValue().set(KEY_PREFIX+orderToken, JSON.toJSONString(lockVos),30, TimeUnit.HOURS);

        //锁定库存成功之后 发送延时消息 stock.ttl 定时解锁
        rabbitTemplate.convertAndSend("ORDER.EXCHANGE","stock.ttl",orderToken);

        //如果都锁定成功返回null
        return null;
    }

    //校验并锁库存
    private void lockStock(SkuLockVo lockVo){
        //通过分布式锁保证原子性
        RLock fairLock = redissonClient.getFairLock(LOCK_PREFIX + lockVo.getSkuId());
        fairLock.lock();

        try {
            // 验库存：查询库存
            List<WareSkuEntity> wareSkuEntities = wareSkuMapper.check(lockVo.getSkuId(),lockVo.getCount());
            //没有仓库 库存不足
            if (CollectionUtils.isEmpty(wareSkuEntities)){
                //锁定库存失败
                lockVo.setLock(false);
            }
            // 锁库存：更新库存
            WareSkuEntity wareSkuEntity = wareSkuEntities.get(0); //大部分情况下根据分析取最近的一个仓库 这里取第一个
            Integer lockLow = wareSkuMapper.lock(lockVo.getCount(), wareSkuEntity.getId());
            if (lockLow == 1){
                //锁定成功
                lockVo.setLock(true);
                //记录锁定库存的仓库
                lockVo.setWareId(wareSkuEntity.getId());
            }else {
                lockVo.setLock(false);
            }
        } finally {
            //解锁
            fairLock.unlock();
        }
    }
}